package controllers

import (
	"beego/models"
	"github.com/astaxie/beego"
	"github.com/astaxie/beego/orm"
	// "github.com/astaxie/beego/logs"
	"github.com/beego/beego/v2/core/logs"
	"golang.org/x/crypto/bcrypt"
	"math/rand"
	"strings"
	"time"
)

type UserController struct {
	beego.Controller
}

func (this *UserController) Get() {
	this.Ctx.WriteString("User")
}

func (this *UserController) Register() {
	this.TplName = "register.html"
}

func (this *UserController) HandleRegister() {
	// 1. 接收请求参数
	username := strings.Trim(this.GetString("username"), "\t\r\n ")
	password := strings.Trim(this.GetString("password"), "\t\r\n ")
	// 2. 校验参数
	if username == "" || password == "" {
		logs.Info("数据不能为空")
		this.Ctx.WriteString("用户名或密码数据不能为空")
		return
	}
	// 3. 插入数据库
	o := orm.NewOrm()

	user := models.User{}
	user.Name = username
	// 3.1 查询用户名是否存在
	err := o.Read(&user, "Name")
	if err != nil {
		if err.Error() != "<QuerySeter> no row found" {
			logs.Info("Read err: ", err)
			this.Redirect("register", 302)
			return
		}
	} else {
		// 提示用户已经注册
		logs.Info("用户已经注册")
		this.Ctx.WriteString("用户已经注册")
		return
	}

	// 加密存储密码
	hashUserPassword, err := hashPassword([]byte(password))
	if err != nil {
		logs.Info("hashPassword err: ", err)
		this.Redirect("register", 302)
		return
	}
	user.Password = hashUserPassword
	// 3.1 先插入 profile
	profile := models.Profile{Age: int16(random(20, 60))}
	_, err = o.Insert(&profile)
	if err != nil {
		logs.Info("插入数据失败", err)
		this.Redirect("register", 302)
		return
	}
	// 3.2 设置User关联的profile_id
	user.Profile = &profile
	_, err = o.Insert(&user)
	if err != nil {
		logs.Info("插入数据失败", err)
		this.Redirect("register", 302)
		return
	}
	// 4. 返回登录成功
	this.Ctx.WriteString("注册成功")
}

func (this *UserController) Login() {
	logs.Info("登录操作。。。")
	this.TplName = "login.html"
}

func (this *UserController) HandleLogin() {
	// u.Ctx.WriteString("登录成功")
	// 1. 接收请求参数
	username := strings.Trim(this.GetString("username"), "\t\r\n ")
	password := strings.Trim(this.GetString("password"), "\t\r\n ")
	// 2. 校验参数
	if username == "" || password == "" {
		logs.Info("数据不能为空")
		this.Ctx.WriteString("用户名或密码数据不能为空")
		return
	}
	// 3. 查询数据 -- 只根据 Name 查询，然后比较密码hash是否正确
	o := orm.NewOrm()
	// user := models.User{Name: username, Password: password}
	// err := o.Read(&user, "Name", "Password")
	user := models.User{Name: username}
	err := o.Read(&user, "Name")
	if err != nil {
		logs.Info("数据不能为空")
		this.Ctx.WriteString("查询失败")
		return
	}
	// 3.1 比较密码hash是否正确
	res, err := verifyPassword(user.Password, []byte(password))
	if !res {
		this.Ctx.WriteString("查询失败")
		return
	}
	// 3.2 保存 session
	this.SetSession("username", username)
	this.SetSession("userId", user.Id)
	// 4. 判断
	// u.Ctx.WriteString("登录成功，欢迎: " + user.Name)
	this.Redirect("/article", 302)
}

func (this *UserController) Logout() {
	this.DelSession("username")
	this.DelSession("userId")
	this.Redirect("/login", 302)
}

// hashPassword 先随机生成salt，salt跟password进行hash
func hashPassword(pwd []byte) (string, error) {
	hash, err := bcrypt.GenerateFromPassword(pwd, bcrypt.MinCost)
	if err != nil {
		return "", err
	}
	return string(hash), nil
}

// verifyPassword 校验hash密码
func verifyPassword(hashedPwd string, plainPwd []byte) (bool, error) {
	byteHash := []byte(hashedPwd)

	err := bcrypt.CompareHashAndPassword(byteHash, plainPwd)
	if err != nil {
		return false, err
	}
	return true, nil
}

func random(min, max int) int {
	rand.Seed(time.Now().Unix())
	i := rand.Intn(max-min) + min
	return i
}
